package metrics_cli

import (
	"context"
	"fmt"
	etcd2 "gluenet/modules/manager/server/adapter/metrics-cli/etcd"
	jaeger2 "gluenet/modules/manager/server/adapter/metrics-cli/jaeger"
	"gluenet/modules/manager/server/adapter/metrics-cli/prom"
	"gluenet/modules/manager/server/domain/dependency"
	"gluenet/modules/manager/server/domain/entity"
	"gluenet/modules/manager/server/infra"
	"gluenet/modules/manager/server/infra/prepare"
	"gopkg.in/yaml.v3"
	"path/filepath"
	"time"
)

func init() {
	prepare.RegisterRepo(func() {
		cli := NewMetricsCLI()
		prepare.InjectDependency(func() dependency.MetricCLI {
			return cli
		})
	})
}

func NewMetricsCLI() *Metrics {
	r := &Metrics{}
	return r
}

type Metrics struct {
}

func (m Metrics) GetInstanceState(GUID string) error {
	panic("implement me")
}

func (m Metrics) GetInstanceMetric(obj *entity.Instance, metric string) error {
	//data,err := prom.CLI.GetMetrics(map[string]string{
	//	"__name__":"info",
	//	"application_guid":obj.GUID,
	//	"platform":obj.Platform,
	//	"object":"application",
	//},fmt.Sprintf("%v",time.Now().Unix()))
	//if err !=nil {
	//	return err
	//}
	//containers := make(map[string]map[string]interface{},len(data.Result))
	//for _,v := range data.Result {
	//	contain := v.Metric["container"]
	//	nodeName := v.Metric["host_name"]
	//	containers[contain]= map[string]interface{}{
	//		"node_name": nodeName,
	//	}
	//}
	//obj.RunTimeRepoValue = obj
	return nil
}

func (m Metrics) UpdateInstanceContainerMetric(obj *entity.Instance) error {
	nodeNames, err := prom.CLI.GetMetrics(map[string]string{
		"__name__":         "info",
		"application_guid": obj.GUID,
		"platform":         obj.Platform,
		//"object":"application",
		"label": "agent_containers",
	}, fmt.Sprintf("%v", time.Now().Unix()))
	if err != nil {
		return err
	}
	status, err := prom.CLI.GetMetrics(map[string]string{
		"__name__":         "status",
		"application_guid": obj.GUID,
		"platform":         obj.Platform,
		//"object":"application",
		"label": "container_status_information",
	}, fmt.Sprintf("%v", time.Now().Unix()))
	if err != nil {
		return err
	}

	//agent,ok := obj.RunTimeRepoValue.(map[string]interface{})["agents"]
	//if !ok {
	//	return fmt.Errorf("runtim don't have agent")
	//}

	containers := make(map[string]map[string]interface{}, len(nodeNames.Result))
	containerStatus := make(map[string]int)
	for _, v := range nodeNames.Result {
		contain := v.Metric["container"]
		nodeName := v.Metric["host_name"]
		containers[contain] = map[string]interface{}{
			"node_name": nodeName,
		}
		containerStatus[contain] = infra.NotReady
	}

	for _, v := range status.Result {
		contain := v.Metric["container"]
		containerStatus[contain] = infra.Ready
	}

	fmt.Printf("obj: %v %v %v\n", obj.Name, containers, containerStatus)

	obj.RunTimeRepoValue.(map[string]interface{})["containers"] = containers
	obj.RunTimeRepoValue.(map[string]interface{})["state"] = containerStatus
	return err
}

func (m Metrics) GetInstanceGraphJaeger(obj *entity.Instance) (interface{}, error) {
	if obj.GUID == "" {
		return nil, fmt.Errorf("key is null")
	}
	var key = filepath.ToSlash(filepath.Join(infra.JaegerDagRepoKey, obj.GUID))

	res, err := etcd2.DB.Get(context.Background(), key)
	if err != nil {
		return nil, err
	}
	if len(res.Kvs) > 1 {
		return nil, fmt.Errorf("much key in etcd")
	}
	if len(res.Kvs) == 0 {
		return nil, infra.DataNotFind
	}

	//fmt.Printf("\n\n%s\n\n",res.Kvs[0].Value)

	var value interface{}
	if err = yaml.Unmarshal(res.Kvs[0].Value, &value); err != nil {
		return nil, err
	}
	return value, nil
}

func (m Metrics) GetInstanceGraphLinkerd(obj *entity.Instance) (interface{}, error) {
	if obj.GUID == "" {
		return nil, fmt.Errorf("key is null")
	}
	var key = filepath.ToSlash(filepath.Join(infra.LinkerdDagRepoKey, obj.GUID))

	res, err := etcd2.DB.Get(context.Background(), key)
	if err != nil {
		return nil, err
	}
	if len(res.Kvs) > 1 {
		return nil, fmt.Errorf("much key in etcd")
	}
	if len(res.Kvs) == 0 {
		return nil, infra.DataNotFind
	}

	//fmt.Printf("\n\n%s\n\n",res.Kvs[0].Value)

	var value interface{}
	if err = yaml.Unmarshal(res.Kvs[0].Value, &value); err != nil {
		return nil, err
	}
	return value, nil
}

func (m Metrics) UpdateInstanceGraphLinkerd(obj *entity.Instance) error {
	if obj.Platform == entity.PlatformGluenets {
		return fmt.Errorf("gluenet not support linkerd")
	}

	var nodes = make(map[string]interface{})
	if runI, ok := obj.RunTimeRepoValue.(map[string]interface{}); !ok {
		return fmt.Errorf("runtime value is not map[string]interface{}")
	} else {
		agent, ok := runI["agents"]
		if !ok {
			return fmt.Errorf("can't not find agent")
		}
		if run, ok := runI["containers"]; !ok {
			return fmt.Errorf("runtime value is not contain containers")
		} else {
			if cts, ok := run.(map[string]interface{}); !ok {
				return fmt.Errorf("contianer is not map[string]interface{}")
			} else {
				for contain, info := range cts {
					nodes[contain] = map[string]interface{}{
						"type":           "container",
						"info":           info,
						"node_guid":      agent,
						"node_type":      "machine",
						"platfrom":       obj.Platform,
						"container_name": contain,
					}
				}
			}
		}
	}

	tcpOpen, err := prom.CLI.GetMetrics(map[string]string{
		"__name__":         "metrics",
		"application_guid": obj.GUID,
		"platform":         obj.Platform,
		//"object":"application",
		"label": "tcp_open_total",
	}, fmt.Sprintf("%v", time.Now().Unix()))
	if err != nil {
		return err
	}
	var links = make([]interface{}, len(tcpOpen.Result))
	for k, v := range tcpOpen.Result {
		source := v.Metric["container"]
		target := v.Metric["dst_container"]
		value := v.Value[1]
		link := map[string]interface{}{
			"source": source,
			"target": target,
			"value":  value,
		}
		links[k] = link
	}

	dag := map[string]interface{}{
		"dag_nodes": nodes,
		"dag_links": links,
	}

	bts, err := yaml.Marshal(dag)
	if err != nil {
		return err
	}
	_, err = etcd2.DB.Put(context.Background(), filepath.ToSlash(filepath.Join(infra.LinkerdDagRepoKey, obj.GUID)), string(bts))
	return err
}

func (m Metrics) UpdateInstanceGraphJaeger(obj *entity.Instance) error {
	switch obj.Platform {
	case entity.PlatformKubernetes:
		var nodes = make(map[string]interface{})
		if runI, ok := obj.RunTimeRepoValue.(map[string]interface{}); !ok {
			return fmt.Errorf("runtime value is not map[string]interface{}")
		} else {
			agent, ok := runI["agents"]
			if !ok {
				return fmt.Errorf("can't not find agent")
			}
			if run, ok := runI["containers"]; !ok {
				return fmt.Errorf("runtime value is not contain containers")
			} else {
				if cts, ok := run.(map[string]interface{}); !ok {
					return fmt.Errorf("contianer is not map[string]interface{}")
				} else {
					for contain, info := range cts {
						nodes[contain] = map[string]interface{}{
							"type":           "container",
							"info":           info,
							"node_guid":      agent,
							"node_type":      "machine",
							"platfrom":       obj.Platform,
							"container_name": contain,
						}
					}
				}
			}
		}
		dagLink, err := jaeger2.CLI.DrawK8sLink(obj.GUID)
		//dagLink,err := jaeger.CLI.DrawK8sLink("f18af130113b0db7554d46d9a994b873")
		if err != nil {
			return err
		}

		dag := map[string]interface{}{
			"dag_nodes": nodes,
			"dag_links": dagLink,
		}

		bts, err := yaml.Marshal(dag)
		if err != nil {
			return err
		}
		_, err = etcd2.DB.Put(context.Background(), filepath.ToSlash(filepath.Join(infra.JaegerDagRepoKey, obj.GUID)), string(bts))
		return err
	case entity.PlatformGluenets:
		var containers []string
		var nodes = make(map[string]interface{})
		if runI, ok := obj.RunTimeRepoValue.(map[string]interface{}); !ok {
			return fmt.Errorf("runtime value is not map[string]interface{}")
		} else {
			agentsI, ok := runI["agents"]
			if !ok {
				return fmt.Errorf("can't not find agent")
			}
			agents, ok := agentsI.(map[string]interface{})
			if !ok {
				return fmt.Errorf("agents is not map")
			}

			if run, ok := runI["containers"]; !ok {
				return fmt.Errorf("runtime value is not contain containers")
			} else {
				if cts, ok := run.(map[string]interface{}); !ok {
					return fmt.Errorf("contianer is not map[string]interface{}")
				} else {
					for contain, info := range cts {
						containers = append(containers, contain)
						nodes[contain] = map[string]interface{}{
							"type":           "container",
							"info":           info,
							"node_guid":      agents[contain],
							"node_type":      "machine",
							"platfrom":       obj.Platform,
							"container_name": contain,
						}
					}
				}
			}
		}
		dagLink, err := jaeger2.CLI.DrawGlueLink(obj.GUID, containers)
		if err != nil {
			return err
		}

		dag := map[string]interface{}{
			"dag_nodes": nodes,
			"dag_links": dagLink,
		}

		bts, err := yaml.Marshal(dag)
		if err != nil {
			return err
		}
		_, err = etcd2.DB.Put(context.Background(), filepath.ToSlash(filepath.Join(infra.JaegerDagRepoKey, obj.GUID)), string(bts))
		return err
	}
	return fmt.Errorf("unknown platform")
}

func (m Metrics) GetInstanceContainerState(GUID, container string) error {
	panic("implement me")
}

func (m Metrics) GetInstanceContainerMetric(GUID, container, metric string) error {
	panic("implement me")
}

func (m Metrics) GetInstanceContainerLog(GUID, container, metric string) error {
	panic("implement me")
}

func (m Metrics) GetAgentState(GUID string) error {
	panic("implement me")
}

func (m Metrics) GetAgentMetric(GUID, metric string) error {
	panic("implement me")
}

func (m Metrics) GetAgentHostState(GUID, hostname string) error {
	panic("implement me")
}

func (m Metrics) GetAgentHostMetric(GUID, hostname, metric string) error {
	panic("implement me")
}
